#include <dspSimulator/instruction/addk.h>

bool ADDK::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void ADDK::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                             std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
    llvm::FunctionType *funcType = llvm::FunctionType::get(irBuilder->getVoidTy(), false);
    // ---------------------------------ADDK (.unit) scst16, dst ---------------------------------------------
    {
        llvm::Function *addkS = llvm::Function::Create(funcType, llvm::GlobalValue::ExternalLinkage, "ADDKKind1",
                                                       module);
        llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(*llvmContext, "entry", addkS);
        irBuilder->SetInsertPoint(entryBB);

        auto dstRegPtr = getDstLoRegPtr(module, irBuilder);
        auto dstReg = irBuilder->CreateLoad(dstRegPtr);

        auto cst16 = getCstN(module, irBuilder, 16);
        auto scst16 = irBuilder->CreateSExt(cst16, irBuilder->getInt32Ty());

        auto res = irBuilder->CreateAdd(scst16, dstReg);
        irBuilder->CreateStore(res, dstRegPtr);
        irBuilder->CreateRetVoid();
    }
}

std::shared_ptr<Instruction> ADDK::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<ADDK> addk;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<5>(data, 2, 0b10100) && existStandardCondition(data)) {
            addk = std::make_shared<ADDK>(dspInstInfo->getAddress(), baseCycle);
            addk->creg_z = extractBits<4>(data, 28);
            addk->dst = extractBits<5>(data, 23).to_ulong();
            addk->cst = extractBits<16>(data, 7).to_ulong();
            addk->s = data[1];
            addk->p = data[0];
            if (addk->s == 0) {
                addk->funcUnit = FuncUnit::S1;
            } else {
                addk->funcUnit = FuncUnit::S2;
            }
            addk->instFormat = InstFormat::Kind1;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (data[10] == 1 && checkBits<6>(data, 1, 0b010111)) {
            addk = std::make_shared<ADDK>(dspInstInfo->getAddress(), baseCycle);
            addk->isCompactInstruction = true;
            addk->compactInstKind = "Sx5";
            addk->dst = extractBits<3>(data, 7).to_ulong();
            addk->cst = extractBits<3>(data, 13).to_ulong();
            addk->cst += extractBits<2>(data, 11).to_ulong() << 3;
            addk->s = data[0];
            if (addk->s == 0) {
                addk->funcUnit = FuncUnit::S1;
            } else {
                addk->funcUnit = FuncUnit::S2;
            }
            addk->PROT = dspInstInfo->getPROT();
            addk->SAT = dspInstInfo->getSAT();
            addk->BR = dspInstInfo->getBR();
            addk->RS = dspInstInfo->getRS();
            addk->DSZ = dspInstInfo->getDSZ();
            addk->p = dspInstInfo->getP();
            addk->fixUpRegOnCompactInstruction();
            addk->instFormat = InstFormat::Kind1;
        }
    }
    return addk;
}

void ADDK::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {

}

std::shared_ptr<DspInstruction> ADDK::clone() {
    return std::make_shared<ADDK>(*this);
}

std::string ADDK::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " " + dec2hex(extractBitsAndToInt<16>(cst)) + "," +
           getDstReg32Name();
    return res;
}
